/*
* 文件：Log.cs
* 作者：王阳
* 时间：2019-5-20
* 描述：日志工具
*/

#define ENABLED_DEBUG
#define ENABLED_WARNING
#define ENABLED_ERROR
#define ENABLED_EVENT

//#define ENABLED_FILE
//#define ENABLED_CONSOLE

using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using System.Threading;

/// <summary>
/// 日志工具
/// </summary>
public static class Log
{
#if ENABLED_FILE || ENABLED_CONSOLE
    [UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.SubsystemRegistration)]
    private static void SubsystemRegistration()
    {
#if ENABLED_FILE
        UnityX.LogFile.Init();
#endif
#if ENABLED_CONSOLE
        UnityX.Console.Init();
#endif
        UnityEngine.Application.logMessageReceivedThreaded += UnityLogHandler;
    }
    private static void UnityLogHandler(string content, string stack, UnityEngine.LogType type)
    {
#if UNITY_EDITOR
        switch (type)
        {
            case UnityEngine.LogType.Log:
                if (Regex.IsMatch(content, @"^<color=#00FF00FF>"))
                {
                    content = content.Replace("<color=#00FF00FF>", "").Replace("</color>", "");
                    type = UnityEngine.LogType.Assert;
                }
                break;
            case UnityEngine.LogType.Warning:
                if (Regex.IsMatch(content, @"^<color=#FFEB04FF>"))
                {
                    content = content.Replace("<color=#FFEB04FF>", "").Replace("</color>", "");
                }
                break;
            case UnityEngine.LogType.Error:
                if (Regex.IsMatch(content, @"^<color=#FF0000FF>"))
                {
                    content = content.Replace("<color=#FF0000FF>", "").Replace("</color>", "");
                }
                break;
            default:
                break;
        }
#else
        if (type == UnityEngine.LogType.Log && Regex.IsMatch(content, @"^#Event#"))
        {
            content = content.Remove(0, 7);
            type = UnityEngine.LogType.Assert;
        }
#endif
        DateTime dateTime = DateTime.Now;
#if ENABLED_FILE
        UnityX.LogFile.Write(content, dateTime, stack, type);
#endif
#if ENABLED_CONSOLE
        UnityX.Console.Write(content, dateTime, stack, type);
#endif
    }
#endif

    /// <summary>
    /// 记录调试信息
    /// </summary>
    /// <param name="obj"></param>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void Debug(object obj)
    {
#if UNITY_EDITOR || ENABLED_DEBUG
        UnityEngine.Debug.Log(ToString(obj));
#endif
    }
    /// <summary>
    /// 记录调试信息
    /// </summary>
    /// <param name="text"></param>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void Debug(string text)
    {
#if UNITY_EDITOR || ENABLED_DEBUG
        UnityEngine.Debug.Log(text);
#endif
    }
    /// <summary>
    /// 记录调试信息
    /// </summary>
    /// <param name="obj"></param>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void Info(object obj)
    {
#if UNITY_EDITOR || ENABLED_DEBUG
        UnityEngine.Debug.Log(ToString(obj));
#endif
    }
    /// <summary>
    /// 记录调试信息
    /// </summary>
    /// <param name="text"></param>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void Info(string text)
    {
#if UNITY_EDITOR || ENABLED_DEBUG
        UnityEngine.Debug.Log(text);
#endif
    }

    /// <summary>
    /// 记录警告信息
    /// </summary>
    /// <param name="obj"></param>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void Warning(object obj)
    {
#if UNITY_EDITOR
        UnityEngine.Debug.LogWarning(TextAddColor(ToString(obj), UnityEngine.Color.yellow));
#elif ENABLED_ERROR
        UnityEngine.Debug.LogWarning(ToString(obj));
#endif
    }
    /// <summary>
    /// 记录警告信息
    /// </summary>
    /// <param name="text"></param>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void Warning(string text)
    {
#if UNITY_EDITOR
        UnityEngine.Debug.LogWarning(TextAddColor(text, UnityEngine.Color.yellow));
#elif ENABLED_ERROR
        UnityEngine.Debug.LogWarning(text);
#endif
    }

    /// <summary>
    /// 记录错误信息
    /// </summary>
    /// <param name="obj"></param>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void Error(object obj)
    {
#if UNITY_EDITOR
        UnityEngine.Debug.LogError(TextAddColor(ToString(obj), UnityEngine.Color.red));
#elif ENABLED_ERROR
        UnityEngine.Debug.LogError(ToString(obj));
#endif
    }
    /// <summary>
    /// 记录错误信息
    /// </summary>
    /// <param name="text"></param>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void Error(string text)
    {
#if UNITY_EDITOR
        UnityEngine.Debug.LogError(TextAddColor(text, UnityEngine.Color.red));
#elif ENABLED_ERROR
        UnityEngine.Debug.LogError(text);
#endif
    }

    /// <summary>
    /// 记录事件信息
    /// </summary>
    /// <param name="obj"></param>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void Event(object obj)
    {
#if UNITY_EDITOR
        UnityEngine.Debug.Log(TextAddColor(ToString(obj), UnityEngine.Color.green));
#else
        UnityEngine.Debug.Log("#Event#" + ToString(obj));
#endif
    }
    /// <summary>
    /// 记录事件信息
    /// </summary>
    /// <param name="text"></param>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void Event(string text)
    {
#if UNITY_EDITOR
        UnityEngine.Debug.Log(TextAddColor(text, UnityEngine.Color.green));
#else
        UnityEngine.Debug.Log("#Event#" + text);
#endif
    }

    private static string TextAddColor(string text, UnityEngine.Color color)
    {
        string colorstr = UnityEngine.ColorUtility.ToHtmlStringRGBA(color);
        if (text.Length < 12000)
        {
            return "<color=#" + colorstr + ">" + new Regex(@"\r?\n").Replace(text, "</color>\n<color=#" + colorstr + ">", 10) + "</color>";
        }
        else
        {
            if (text.Contains("\n"))
            {
                return "<color=#" + colorstr + ">" + new Regex(@"\r?\n").Replace(text, "</color>\n", 1);
            }
            else
            {
                return "<color=#" + colorstr + ">" + text.Substring(0, 100) + "</color>" + text.Remove(0, 100);
            }
        }
    }

    /// <summary>
    /// 对象转排版字符串
    /// </summary>
    /// <param name="obj">对象</param>
    /// <param name="maxDepth">包含对象最大深度</param>
    /// <returns></returns>
    public static string ToString(object obj, int maxDepth = 8)
    {
        return WriteVal(new Type[maxDepth], -1, obj, "");
    }
    private static string WriteVal(Type[] typeList, int typeIndex, object obj, string space)
    {
        if (obj == null)
        {
            return "null";
        }
        if (obj is sbyte ||
            obj is byte ||
            obj is short ||
            obj is ushort ||
            obj is int ||
            obj is uint ||
            obj is long ||
            obj is ulong ||
            obj is float ||
            obj is double ||
            obj is bool ||
            obj is DateTime ||
            obj is Enum)
        {
            return obj.ToString();
        }
        if (obj is char)
        {
            return "\'" + obj + "\'";
        }
        if (obj is string)
        {
            return "\"" + obj + "\"";
        }

        if (obj is UnityEngine.Vector2)
        {
            UnityEngine.Vector2 o = (UnityEngine.Vector2)obj;
            return "{x=" + o.x + " y=" + o.y + "}";
        }
        if (obj is UnityEngine.Vector3)
        {
            UnityEngine.Vector3 o = (UnityEngine.Vector3)obj;
            return "{x=" + o.x + " y=" + o.y + " z=" + o.z + "}";
        }
        if (obj is UnityEngine.Vector4)
        {
            UnityEngine.Vector4 o = (UnityEngine.Vector4)obj;
            return "{x=" + o.x + " y=" + o.y + " z=" + o.z + " w=" + o.w + "}";
        }
        if (obj is UnityEngine.Quaternion)
        {
            UnityEngine.Quaternion o = (UnityEngine.Quaternion)obj;
            return "{x=" + o.x + " y=" + o.y + " z=" + o.z + " w=" + o.w + "}";
        }
        if (obj is UnityEngine.Color)
        {
            UnityEngine.Color o = (UnityEngine.Color)obj;
            return "{r=" + o.r + " g=" + o.g + " b=" + o.b + " a=" + o.a + "}";
        }
        if (obj is UnityEngine.Color32)
        {
            UnityEngine.Color32 o = (UnityEngine.Color32)obj;
            return "{r=" + o.r + " g=" + o.g + " b=" + o.b + " a=" + o.a + "}";
        }
        if (obj is UnityEngine.Rect)
        {
            UnityEngine.Rect o = (UnityEngine.Rect)obj;
            return "{x=" + o.x + " y=" + o.y + " width=" + o.width + " height=" + o.height + "}";
        }
        if (obj is UnityEngine.Matrix4x4)
        {
            string s = "";
            UnityEngine.Matrix4x4 o = (UnityEngine.Matrix4x4)obj;
            s += "\n" + space + "{";
            s += "\n" + space + ".   m00=" + o.m00 + " m10=" + o.m10 + " m20=" + o.m20 + " m30=" + o.m30;
            s += "\n" + space + ".   m01=" + o.m01 + " m11=" + o.m11 + " m21=" + o.m21 + " m31=" + o.m31;
            s += "\n" + space + ".   m02=" + o.m02 + " m12=" + o.m12 + " m22=" + o.m22 + " m32=" + o.m32;
            s += "\n" + space + ".   m03=" + o.m03 + " m13=" + o.m13 + " m23=" + o.m23 + " m33=" + o.m33;
            s += "\n" + space + "}";
            return s;
        }

        // 数组
        if (obj is Array)
        {
            if (obj is sbyte[])
            {
                return WriteArray((sbyte[])obj);
            }
            else if (obj is byte[])
            {
                return WriteArray((byte[])obj);
            }
            else if (obj is short[])
            {
                return WriteArray((short[])obj);
            }
            else if (obj is ushort[])
            {
                return WriteArray((ushort[])obj);
            }
            else if (obj is int[])
            {
                return WriteArray((int[])obj);
            }
            else if (obj is uint[])
            {
                return WriteArray((uint[])obj);
            }
            else if (obj is long[])
            {
                return WriteArray((long[])obj);
            }
            else if (obj is ulong[])
            {
                return WriteArray((ulong[])obj);
            }
            else if (obj is float[])
            {
                return WriteArray((float[])obj);
            }
            else if (obj is double[])
            {
                return WriteArray((double[])obj);
            }
            else if (obj is bool[])
            {
                return WriteArray((bool[])obj);
            }
            // 其他类型数组
            else
            {
                Array array = (Array)obj;
                string str = "\n" + space + "[\n";
                foreach (object item in array)
                {
                    str += space + ".   " + WriteVal(typeList, typeIndex, item, space + ".   ") + ",\n";
                }
                return str + space + "]";
            }
        }
        // 链表
        if (obj is IList)
        {
            // byte链表
            if (obj is List<sbyte>)
            {
                return WriteList((List<sbyte>)obj);
            }
            else if (obj is List<byte>)
            {
                return WriteList((List<byte>)obj);
            }
            else if (obj is List<short>)
            {
                return WriteList((List<short>)obj);
            }
            else if (obj is List<ushort>)
            {
                return WriteList((List<ushort>)obj);
            }
            else if (obj is List<int>)
            {
                return WriteList((List<int>)obj);
            }
            else if (obj is List<uint>)
            {
                return WriteList((List<uint>)obj);
            }
            else if (obj is List<long>)
            {
                return WriteList((List<long>)obj);
            }
            else if (obj is List<ulong>)
            {
                return WriteList((List<ulong>)obj);
            }
            else if (obj is List<float>)
            {
                return WriteList((List<float>)obj);
            }
            else if (obj is List<double>)
            {
                return WriteList((List<double>)obj);
            }
            else if (obj is List<bool>)
            {
                return WriteList((List<bool>)obj);
            }
            // 其他类型链表
            else
            {
                IList list = (IList)obj;
                string str = "\n" + space + "[\n";
                foreach (object item in list)
                {
                    str += space + ".   " + WriteVal(typeList, typeIndex, item, space + ".   ") + ",\n";
                }
                return str + space + "]";
            }
        }
        // 表示键/值对的集合
        if (obj is IDictionary)
        {
            IDictionary dictionary = (IDictionary)obj;
            string str = "\n" + space + "[\n";
            foreach (DictionaryEntry item in dictionary)
            {
                str += space + ".   " + WriteVal(typeList, typeIndex, item.Key, space + ".   ");
                str += " : ";
                str += WriteVal(typeList, typeIndex, item.Value, space + ".   ") + ",\n";
            }
            return str + space + "]";
        }
        typeIndex++;
        if (typeIndex < typeList.Length)
        {
            if (obj is Type)
            {
                typeList[typeIndex] = (Type)obj;
                return WriteType(typeList, typeIndex, space);
            }
            if (obj is UnityEngine.GameObject)
            {
                Type[] newTypeList = new Type[typeList.Length + 1];
                for (int i = 0; i < typeList.Length; i++)
                {
                    newTypeList[i] = typeList[i];
                }
                newTypeList[typeIndex] = typeof(UnityEngine.SceneManagement.Scene);
                typeIndex++;
                newTypeList[typeIndex] = obj.GetType();
                return WriteObj(newTypeList, typeIndex, obj, space);
            }
            if (obj is UnityEngine.Transform)
            {
                Type[] newTypeList = new Type[typeList.Length + 2];
                for (int i = 0; i < typeList.Length; i++)
                {
                    newTypeList[i] = typeList[i];
                }
                newTypeList[typeIndex] = typeof(UnityEngine.GameObject);
                typeIndex++;
                newTypeList[typeIndex] = typeof(UnityEngine.Matrix4x4);
                typeIndex++;
                newTypeList[typeIndex] = obj.GetType();
                return WriteObj(newTypeList, typeIndex, obj, space);
            }
            if (obj is UnityEngine.MonoBehaviour)
            {
                Type[] newTypeList = new Type[typeList.Length + 3];
                for (int i = 0; i < typeList.Length; i++)
                {
                    newTypeList[i] = typeList[i];
                }
                newTypeList[typeIndex] = typeof(UnityEngine.GameObject);
                typeIndex++;
                newTypeList[typeIndex] = typeof(UnityEngine.Transform);
                typeIndex++;
                newTypeList[typeIndex] = typeof(CancellationToken);
                typeIndex++;
                newTypeList[typeIndex] = obj.GetType();
                return WriteObj(newTypeList, typeIndex, obj, space);
            }
            typeList[typeIndex] = obj.GetType();
            return WriteObj(typeList, typeIndex, obj, space);
        }
        return "<深度超标>";
    }
    private static string WriteObj(Type[] typeList, int typeIndex, object obj, string space)
    {
        string str = "\n" + space + "{\n";
        bool isJX;
        // 遍历字段
        foreach (FieldInfo fieldInfo in typeList[typeIndex].GetFields(BindingFlags.Public | BindingFlags.Instance))
        {
            // 防止递归锁死
            isJX = true;
            for (int i = 0; i <= typeIndex; i++)
            {
                if (fieldInfo.FieldType == typeList[i])
                {
                    isJX = false;
                    break;
                }
            }
            // 防止递归锁死
            if (isJX)
            {
                try
                {
                    str += space + ".   " + fieldInfo.Name + " = " + WriteVal(typeList, typeIndex, fieldInfo.GetValue(obj), space + ".   ") + "\n";
                }
                catch
                {
                }
            }
            else
            {
                str += space + ".   " + fieldInfo.Name + " = <循环终止>\n";
            }
        }
        // 遍历属性
        foreach (PropertyInfo propertyInfo in typeList[typeIndex].GetProperties(BindingFlags.Public | BindingFlags.Instance))
        {
            // 防止递归锁死
            isJX = true;
            for (int i = 0; i <= typeIndex; i++)
            {
                if (propertyInfo.PropertyType == typeList[i])
                {
                    isJX = false;
                    break;
                }
            }
            // 防止递归锁死
            if (isJX)
            {
                try
                {
                    str += space + ".   " + propertyInfo.Name + " = " + WriteVal(typeList, typeIndex, propertyInfo.GetValue(obj), space + ".   ") + "\n";
                }
                catch
                {
                }
            }
            else
            {
                str += space + ".   " + propertyInfo.Name + " = <循环终止>\n";
            }
        }
        return str + space + "}";
    }
    private static string WriteType(Type[] type, int index, string space)
    {
        string str = "\n" + space + "{\n";
        bool isJX;
        // 遍历字段
        foreach (FieldInfo fieldInfo in type[index].GetFields(BindingFlags.Public | BindingFlags.Static))
        {
            isJX = true;
            for (int i = 0; i <= index; i++)
            {
                // 防止递归锁死
                if (fieldInfo.FieldType == type[i])
                {
                    isJX = false;
                    break;
                }
            }
            // 防止递归锁死
            if (isJX)
            {
                try
                {
                    object o = fieldInfo.GetValue(null);
                    str += space + ".   " + fieldInfo.Name + " = " + WriteVal(type, index, o, space + ".   ") + "\n";
                }
                catch
                {
                }
            }
            else
            {
                str += space + ".   " + fieldInfo.Name + " = <循环终止>\n";
            }
        }
        // 遍历属性
        foreach (PropertyInfo propertyInfo in type[index].GetProperties(BindingFlags.Public | BindingFlags.Static))
        {
            isJX = true;
            for (int i = 0; i <= index; i++)
            {
                // 防止递归锁死
                if (propertyInfo.PropertyType == type[i])
                {
                    isJX = false;
                    break;
                }
            }
            // 防止递归锁死
            if (isJX)
            {
                try
                {
                    object o = propertyInfo.GetValue(null);
                    str += space + ".   " + propertyInfo.Name + " = " + WriteVal(type, index, o, space + ".   ") + "\n";
                }
                catch
                {
                }
            }
            else
            {
                str += space + ".   " + propertyInfo.Name + " = <循环终止>\n";
            }
        }
        return str + space + "}";
    }
    private static string WriteArray<T>(T[] array)
    {
        string str = "[";
        if (array.Length > 1000)
        {
            for (int i = 0; i < 100; i++)
            {
                str += array[i] + ",";
            }
            str += "......";
        }
        else
        {
            for (int i = 0; i < array.Length; i++)
            {
                str += array[i] + ",";
            }
        }
        return str + "]";
    }
    private static string WriteList<T>(List<T> list)
    {
        string str = "[";
        if (list.Count > 1000)
        {
            for (int i = 0; i < 100; i++)
            {
                str += list[i] + ",";
            }
            str += "......";
        }
        else
        {
            foreach (T item in list)
            {
                str += item + ",";
            }
        }
        return str + "]";
    }
}